/****************************************************************************
                Университет:    Санкт-Петербургский Политехнический Университет
                Кафедра:        Компьютерные системы и программные технологии
                Аспирант:       Быков Илья Иванович
                Программа:      Расчет коэффициентов распределения
                Модуль:         edge.cpp
                Назначение:
****************************************************************************/

#include <QPainter>
#include <QDebug>
#include <QPolygonF>

#include "edge.h"
#include "node.h"

#include <math.h>

static const qreal arrowSize = 10.;

static const double Pi = 3.14159265358979323846264338327950288419717;
static double TwoPi = 2.0 * Pi;

Edge::Edge(Node *sourceNode, Node *destNode)
    : m_source(sourceNode)
    , m_dest(destNode)
    , m_number(0)
    , m_resist_R (0.0)
    , m_resist_X (0.0)
    , m_power_a(0.0)
    , m_power_r(0.0)
    , m_state (1)
    , m_power_precision(3)
    , m_resistance_visible(true)
{
    setFlag(ItemIsSelectable);
    adjust();
}

Edge::Edge(Node *sourceNode, Node *destNode, int state, double r_r, double r_x, double p_a, double p_r, double otr_pow, double pol_pow)
    : m_source(sourceNode)
    , m_dest(destNode)
    , m_number(0)
    , m_power_precision(3)
    , m_resistance_visible(true)
{
    m_state = state;
    m_resist_R = r_r;
    m_resist_X = r_x;

    m_power_a = p_a;
    m_power_r = p_r;

    m_otr_pow_a = otr_pow;
    m_pol_pow_a = pol_pow;

    setFlag(ItemIsSelectable);
    adjust();
}


Node *Edge::sourceNode() const
{
    return m_source;
}

Node *Edge::destNode() const
{
    return m_dest;
}

Node *Edge::oppositeNode(Node *node) const
{
    return node == m_source ? m_dest : m_source;
}

void Edge::adjust()
{
    if (!m_source || !m_dest)
        return;

    QLineF line(mapFromItem(m_source, 0, 0), mapFromItem(m_dest, 0, 0));
    qreal length = line.length();

    prepareGeometryChange();

    if (length > qreal(20.)) {
        QPointF edgeSourceOffset((line.dx() * m_source->radius()) / length, (line.dy() * m_source->radius()) / length);
        QPointF edgeDestOffset((line.dx() * m_dest->radius()) / length, (line.dy() * m_dest->radius()) / length);
        m_sourcePoint = line.p1() + edgeSourceOffset;
        m_destPoint = line.p2() - edgeDestOffset;
    } 
    else {
        m_sourcePoint = m_destPoint = line.p1();
    }
}

int Edge::number() const
{
    return m_number;
}

void Edge::setNumber(int number)
{
    m_number = number;
    update();
}


void Edge::getResist(double &R, double &X)
{
    R = m_resist_R;
    X = m_resist_X;
}


void Edge::setResist(const double R, const double X)
{
    m_resist_R = R;
    m_resist_X = X;
}

void Edge::getPower(double &pow_a, double &pow_r)
{
    pow_a = m_power_a;
    pow_r = m_power_r;
}

void Edge::setPower(const double pow_a, const double pow_r)
{
    m_power_a = pow_a;
    m_power_r = pow_r;
}

void Edge::getLimitPower(double &otr_pow_a, double &pol_pow_a)
{
    otr_pow_a = m_otr_pow_a;
    pol_pow_a = m_pol_pow_a;
}

void Edge::setLimitPower(double otr_pow_a, double pol_pow_a)
{
    m_otr_pow_a = otr_pow_a;
    m_pol_pow_a = pol_pow_a;
}

void Edge::setPrecision(int precision)
{
    m_power_precision = precision;
}

void Edge::setResistanceState(bool state)
{
     m_resistance_visible = state;
}

int Edge::getstate()
{
    return m_state;
}

void Edge::setState(int state)
{
    m_state = state;
}


bool Edge::lessThan(Edge *e1, Edge *e2)
{
    Node *n1;
    Node *n2;
    if (e1->destNode() == e2->destNode()) {
        n1 = e1->sourceNode();
        n2 = e2->sourceNode();
    }
    else if (e1->sourceNode() == e2->destNode()) {
        n1 = e1->destNode();
        n2 = e2->sourceNode();
    }
    else if (e1->destNode() == e2->sourceNode()) {
        n1 = e1->sourceNode();
        n2 = e2->destNode();
    }
    else {
        n1 = e1->destNode();
        n2 = e2->destNode();
    }
    return n1->number() < n2->number();
}

QRectF Edge::boundingRect() const
{
    if (!m_source || !m_dest)
        return QRectF();

    qreal penWidth = 1;
    qreal extra = (penWidth + arrowSize) / 2.0;

    // Против размазывания экрана
    int delta = 70;

    QRectF rect(m_sourcePoint, QSizeF(m_destPoint.x() - m_sourcePoint.x(),m_destPoint.y() - m_sourcePoint.y()));

    return rect.normalized().adjusted(-extra-delta, -extra-delta, extra+delta, extra+delta);
}

QPainterPath Edge::shape() const
{
    QPainterPath path;
    QLineF line(m_sourcePoint, m_destPoint);
    double angle = ::acos(line.dx() / line.length());
    if (line.dy() >= 0)
        angle = TwoPi - angle;    
    QPointF sourceArrowP1 = m_sourcePoint + QPointF(sin(angle + Pi / 3) * arrowSize,
                                                  cos(angle + Pi / 3) * arrowSize);
    QPointF sourceArrowP2 = m_sourcePoint + QPointF(sin(angle + Pi - Pi / 3) * arrowSize,
                                                  cos(angle + Pi - Pi / 3) * arrowSize);   
    QPointF destArrowP1 = m_destPoint + QPointF(sin(angle - Pi / 3) * arrowSize,
                                              cos(angle - Pi / 3) * arrowSize);
    QPointF destArrowP2 = m_destPoint + QPointF(sin(angle - Pi + Pi / 3) * arrowSize,
                                              cos(angle - Pi + Pi / 3) * arrowSize);
   
    QPolygonF polygon;
    polygon << line.p1() << line.p2() << destArrowP1 << destArrowP2;
    path.addPolygon(polygon);
    return path;
}

void Edge::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
{
    if (!m_source || !m_dest)
        return;

    QLineF line1(m_sourcePoint, m_destPoint);
    line1.setLength(line1.length() / 2 - 10);
    QLineF line2(m_destPoint, m_sourcePoint);
    line2.setLength(line2.length() / 2 - 10);
    if (qFuzzyCompare(line1.length(), qreal(0.)))
        return;

    painter->setRenderHint(QPainter::Antialiasing);
    // Нарисовать саму линию
    QColor color;

    // Выставляем цвет согласно статусу ветви
    if (m_state == 0)
        color = Qt::red;
    else if (m_state == 1)
        color = Qt::darkGreen;
    
    if (isSelected())
        color = Qt::blue;
   // else
   //     color = Qt::black;
    
    if (m_state == 0)
        painter->setPen(QPen(color, 2, Qt::DashLine, Qt::RoundCap, Qt::RoundJoin));
    else
        painter->setPen(QPen(color, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
    painter->drawLine(line1);
    painter->drawLine(line2);
    painter->setRenderHint(QPainter::Antialiasing,false);

    // Нарисовать стрелку
    double angle = ::acos(line1.dx() / line1.length());
    if (line1.dy() >= 0)
        angle = TwoPi - angle;

    QPointF sourceArrowP1 = m_sourcePoint + QPointF(sin(angle + Pi / 3) * arrowSize, cos(angle + Pi / 3) * arrowSize);
    QPointF sourceArrowP2 = m_sourcePoint + QPointF(sin(angle + Pi - Pi / 3) * arrowSize, cos(angle + Pi - Pi / 3) * arrowSize);
    QPointF destArrowP1 = m_destPoint + QPointF(sin(angle - Pi / 3) * arrowSize, cos(angle - Pi / 3) * arrowSize);
    QPointF destArrowP2 = m_destPoint + QPointF(sin(angle - Pi + Pi / 3) * arrowSize, cos(angle - Pi + Pi / 3) * arrowSize);

    // Открисовываем треугольник стрелки
    painter->setBrush(color);
    painter->drawPolygon(QPolygonF() << line2.p1() << destArrowP1 << destArrowP2);

    // Открисовываем номер
    painter->setPen(QPen(Qt::red, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
    painter->drawText(boundingRect(), Qt::AlignCenter, QString::number(m_number));

    painter->save();

    // Создаем фон
    QFont f ("Courier", 10, QFont::Normal);
    f.setStyle(QFont::StyleItalic);
    painter->setFont (f);

    double deg_angle = angle*180/Pi;
    QString str_regist = QString("Z%1 = %2 + j∙%3").arg(QString::number(m_number)).arg(QString::number(m_resist_R)).arg(QString::number(m_resist_X));
    QString str_power = QString("S%1 = %2").arg(QString::number(m_number)).arg(QString::number(m_power_a, 'f', m_power_precision));

    // Размер выводимых строк в пикселях
    QFontMetrics fm(f);
    int size_regist = fm.width(str_regist);
    int size_power = fm.width(str_power);

    // Для удобства чтения (чтобы не читать вверх ногами)
    if (deg_angle>90 && deg_angle<270)
        deg_angle = deg_angle - 180;

    painter->translate((m_destPoint.x() + m_sourcePoint.x())/2,(m_destPoint.y() + m_sourcePoint.y())/2);

    painter->rotate(-deg_angle);

    painter->setPen(Qt::black);

    painter->setRenderHint(QPainter::TextAntialiasing);
    if (m_resistance_visible)
        painter->drawText(-size_regist/2, 15, str_regist);
    painter->drawText(-size_power/2, -11, str_power);
    painter->setRenderHint(QPainter::TextAntialiasing, false);
    painter->restore();

}
